#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>

// 确保POSIX特性（包含文件锁定义）
#define _POSIX_C_SOURCE 200809L

// 如果SA_RESTART未定义，提供替代方案
#ifndef SA_RESTART
#define SA_RESTART 0x10000000
#endif

#define BUFFER_SIZE 1024
#define SEARCH_DIR "/home/search_files" // 搜索目录，可根据需要修改
#define PORT 8888

// 安全发送消息，确保数据完整传输
void send_message(int sockfd, const char *message)
{
    size_t len = strlen(message);
    ssize_t sent = 0;

    printf("开始向客户端发送消息：%s\n", message);
    while (sent < len)
    {
        ssize_t n = send(sockfd, message + sent, len - sent, 0);
        if (n <= 0)
        {
            perror("send failed");
            break;
        }
        sent += n;
    }
    printf("消息发送完成：%s\n", message);
}

// 忽略子进程结束信号，防止僵尸进程
void sigchld_handler(int signo)
{
    printf("捕获到SIGCHLD信号，开始清理子进程...\n");
    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
    printf("子进程清理完成\n");
}

// 检查目录权限
int check_directory_permissions(const char *path)
{
    struct stat dir_stat;
    if (stat(path, &dir_stat) != 0)
    {
        perror("stat");
        printf("获取目录状态信息失败，无法检查权限\n");
        return 0;
    }

    // 检查是否为目录
    if (!S_ISDIR(dir_stat.st_mode))
    {
        fprintf(stderr, "%s不是一个目录\n", path);
        printf("指定路径不是目录，权限检查不通过\n");
        return 0;
    }

    // 检查是否有读权限
    if (access(path, R_OK) != 0)
    {
        fprintf(stderr, "没有读取目录%s的权限\n", path);
        printf("对目录没有读权限，权限检查不通过\n");
        return 0;
    }

    // 检查是否有执行权限(遍历目录需要)
    if (access(path, X_OK) != 0)
    {
        fprintf(stderr, "没有执行目录%s的权限\n", path);
        printf("对目录没有执行权限，权限检查不通过\n");
        return 0;
    }

    printf("目录权限检查通过：%s\n", path);
    return 1;
}

// 自定义比较函数
static int compare_dirents(const struct dirent **a, const struct dirent **b) {
    return strcmp((*a)->d_name, (*b)->d_name);
}

// 递归搜索文件
void search_files(const char *path, const char *filename, int client_socket) {
    DIR *dir;
    struct dirent *entry;
    char full_path[BUFFER_SIZE];
    struct stat statbuf;
    int results_count = 0;
    int dir_fd;           // 新增：目录文件描述符（用于加锁）

    printf("正在目录: %s 中搜索查询: %s\n", path, filename);

    // 打开目录并获取文件描述符（用于flock）
    if (!(dir = opendir(path))) {
        perror("opendir");
        return;
    }
    dir_fd = dirfd(dir);  // 从DIR*获取文件描述符

    // 获取目录独占锁（防止并发访问）
    if (flock(dir_fd, LOCK_EX) != 0) {  // LOCK_EX：独占锁
        perror("flock(LOCK_EX) failed");
        closedir(dir);
        return;
    }

    // 读取所有目录条目到内存中
    struct dirent **entries = NULL;
    int n = scandir(path, &entries, NULL, compare_dirents);
    if (n < 0) {
        perror("scandir");
        closedir(dir);
        flock(dir_fd, LOCK_UN);  // 释放锁
        return;
    }

    // 遍历所有条目
    for (int i = 0; i < n; i++) {
        entry = entries[i];
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name);

        // 使用stat()检查文件类型
        if (stat(full_path, &statbuf) != 0) {
            continue;
        }

        if (S_ISDIR(statbuf.st_mode)) {
            // 递归搜索子目录（同样需要加锁）
            search_files(full_path, filename, client_socket);
        } else if (strstr(entry->d_name, filename) != NULL) {
            // 发送匹配结果
            results_count++;
            send_message(client_socket, full_path);
            send_message(client_socket, "\n");
        }

        free(entry);
    }

    free(entries);
    closedir(dir);
    flock(dir_fd, LOCK_UN);  // 释放目录锁
    printf("在目录%s中找到%d个匹配项\n", path, results_count);
}


// 处理客户端请求
void handle_client(int client_socket)
{
    printf("开始处理客户端请求，客户端套接字：%d\n", client_socket);

    char buffer[BUFFER_SIZE];
    ssize_t bytes_received;

    // 接收客户端搜索请求
    memset(buffer, 0, BUFFER_SIZE);
    bytes_received = recv(client_socket, buffer, BUFFER_SIZE - 1, 0);
    if (bytes_received <= 0)
    {
        perror("recv failed");
        printf("接收客户端请求失败，关闭客户端套接字：%d\n", client_socket);
        close(client_socket);
        return;
    }

    // 移除换行符
    buffer[strcspn(buffer, "\n")] = 0;

    printf("接收到客户端的搜索请求：%s\n", buffer);

    // 检查搜索目录权限
    if (!check_directory_permissions(SEARCH_DIR))
    {
        const char *error_msg = "Search directory permission denied\n";
        send_message(client_socket, error_msg);
        printf("搜索目录权限不足，关闭客户端套接字：%d\n", client_socket);
        close(client_socket);
        return;
    }

    // 搜索文件
    search_files(SEARCH_DIR, buffer, client_socket);

    // 发送结束标记
    const char *end_marker = "END_OF_RESULTS\n";
    send_message(client_socket, end_marker);

    printf("处理客户端请求完成，关闭客户端套接字：%d\n", client_socket);
    close(client_socket);
}

int main()
{
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    pid_t pid;

    // 设置信号处理，防止僵尸进程
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1)
    {
        perror("sigaction");
        printf("设置SIGCHLD信号处理失败，程序退出\n");
        exit(EXIT_FAILURE);
    }

    // 创建套接字
    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket == -1)
    {
        perror("socket creation failed");
        printf("创建服务器套接字失败，程序退出\n");
        exit(EXIT_FAILURE);
    }

    // 设置套接字选项，允许地址重用
    int opt = 1;
    if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
    {
        perror("setsockopt failed");
        printf("设置套接字选项失败，程序退出\n");
        exit(EXIT_FAILURE);
    }

    // 准备服务器地址结构
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    // 绑定套接字
    if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
    {
        perror("bind failed");
        printf("绑定服务器套接字失败，程序退出\n");
        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(server_socket, 5) == -1)
    {
        perror("listen failed");
        printf("监听服务器套接字失败，程序退出\n");
        exit(EXIT_FAILURE);
    }

    printf("服务器正在监听端口 %d...\n", PORT);

    // 主循环，接受客户端连接
    while (1)
    {
        // 接受客户端连接
        client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);
        if (client_socket == -1)
        {
            perror("accept failed");
            printf("接受客户端连接失败，继续等待下一个连接\n");
            continue;
        }

        printf("客户端从 %s:%d 连接\n",
               inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        // 创建子进程处理客户端请求
        pid = fork();
        if (pid < 0)
        {
            perror("fork failed");
            printf("创建子进程失败，关闭客户端套接字：%d\n", client_socket);
            close(client_socket);
        }
        else if (pid == 0)
        {
            // 子进程
            close(server_socket); // 子进程不需要监听套接字
            handle_client(client_socket);
            exit(EXIT_SUCCESS);
        }
        else
        {
            // 父进程
            close(client_socket); // 父进程不需要客户端套接字
        }
    }

    // 关闭服务器套接字
    close(server_socket);
    printf("服务器套接字关闭，程序结束\n");
    return 0;
}